﻿using System;
using System.Linq;
using System.Net.Sockets;
using Wsq.Utils.DoNetFramework40.Socket.tools;

namespace Wsq.Utils.DoNetFramework40.Socket
{
    public abstract class HttpServer
    {
        protected int port;
        Server server;
        //TcpListener listener;
        //bool is_active = true;
        //private Dictionary<string, HttpSession> SessionDic = new Dictionary<string, HttpSession>();
        private string wsPath = "/";
        private int oref=0;
        //private ConcurrentBag<SocketAsyncEventArgs> wsClientList = new ConcurrentBag<SocketAsyncEventArgs>();
        
        public HttpServer(int port,string wsPath="/")
        {
            this.port = port;
            this.wsPath = wsPath;
        }
        public int ClientCount
        {
            get
            {
                return server.ClientCount;
            }
        }

        public void listen()
        {
            server = new Server(1000,1024*100);
            server.ReceiveClientData += OnReceiveClientData; //设置输入代理
            server.ClientConnected += OnClientConnected; //设置连接代理
            server.ClientDisConnected += OnClientDisConnected; //设置 客户端断开
            
            server.Start(new System.Net.IPEndPoint(System.Net.IPAddress.Any,port));
            //listener = new TcpListener(IPAddress.Any, port);
            //listener.Start();
            //while (is_active)
            //{
            //    TcpClient s = listener.AcceptTcpClient();
            //    HttpProcessor processor = new HttpProcessor(s, this);
            //    Thread thread = new Thread(new ThreadStart(processor.process));
            //    thread.Start();
            //    Thread.Sleep(1);
            //}
        }
    

        public  abstract void handleRequest(HttpReadParse readParse, HttpResponse response);
        //public abstract void handlePOSTRequest(HttpReadParse readParse);
        private void OnClientConnected(SocketAsyncEventArgs socketAsync) 
        {
            socketAsync.UserToken = null;
        }
        private void OnClientDisConnected(SocketAsyncEventArgs socketAsync) 
        {
            if (socketAsync.UserToken!=null&& (socketAsync.UserToken as HttpUserToken).type==1)
            {
                WsDisConnected(socketAsync);
            }
            
        }

        /// <summary>
        /// 数据包输入
        /// </summary>
        /// <param name="data">输入数据</param>
        /// <param name="socketAsync">该数据包的通讯SOCKET</param>
        private void OnReceiveClientData(SocketAsyncEventArgs socketAsync,byte[] data)
        {
            if (socketAsync.UserToken == null) //如果此SOCKET绑定的对象为NULL
            {
                //注意这里为了 简单 所以就绑定了个 BuffList 类，本来这里应该绑定用户类对象，
                //并在用户类里面建立 初始化 一个 BuffList 类，这样就能通过用户类保存更多的信息了。
                //比如用户名，权限等等
                socketAsync.UserToken = new HttpUserToken();
            }
            
            if (data.Length > 0)
            {
                DataOn(data, socketAsync);
            }
        }
        private void DataOn(byte[] data, SocketAsyncEventArgs e)
        {
            try
            {
                var userToken = e.UserToken as HttpUserToken;
                if (userToken.type == 1)
                {
                    ReceiveProtocol(data, e);
                }
                else
                {
                    HttpReadParse readParse = userToken.readParse;
                    readParse.AddBuffer(data);
                    if (readParse.IsReadEnd)
                    {
                        readParse.IPAddress = e.AcceptSocket.RemoteEndPoint.ToString();
                        string jSessionID = readParse.Cookie.GetValue("JSessionID");
                        bool addJSessionID = false;
                        if (userToken.HttpSession==null)
                        {
                            userToken.HttpSession = new HttpSession(oref++);
                            addJSessionID = true;
                        }
                        readParse.HttpSession = userToken.HttpSession;
                        var httpResponse = new HttpResponse(e, server, userToken.HttpSession, addJSessionID);
                        if (readParse.HttpHerderDic.ContainsKey("Connection") && readParse.HttpHerderDic["Connection"] == "Upgrade" && readParse.HttpHerderDic["Upgrade"] == "websocket")
                        {
                            if (wsPath == readParse.HttpRequestUrl)
                            {
                                ((HttpUserToken)e.UserToken).type = 1;
                                WsConnected(e);
                                wesocketUpgrade(readParse, httpResponse);
                            }
                            else
                            {
                                httpResponse.WriteFailure();
                                e.UserToken = null;
                                server.CloseClient(e);
                            }
                        }
                        else
                        {
                            handleRequest(readParse, httpResponse);
                            e.UserToken = null;
                            server.CloseClient(e);
                        }

                    }
                }
            }
            catch (Exception er)
            {
                Console.WriteLine(er.ToString());
                server.CloseClient(e);
            }
        }

        private void wesocketUpgrade(HttpReadParse readParse, HttpResponse response) 
        {
            //string Sec_WebSocket_Version = readParse.HttpHerderDic["Sec-WebSocket-Version"];
            string Sec_WebSocket_Key= readParse.HttpHerderDic["Sec-WebSocket-Key"];
            response.WsUpgrade(Sec_WebSocket_Key);
        }
        private void ReceiveProtocol(byte[] re, SocketAsyncEventArgs e)
        {
            byte[] r = null ;
            var userToken = e.UserToken as HttpUserToken;
            if (userToken.tempBuffer==null)
            {
                userToken.tempBuffer = re;
            }
            else
            {
                userToken.tempBuffer = userToken.tempBuffer.Concat(re).ToArray();
            }
            if (userToken.tempBuffer.Length<2)
            {
                return;
            }
            else
            {
                int payload_len = userToken.tempBuffer[1] & 0x7F;
                if (payload_len == 126)
                {
                    if (userToken.tempBuffer.Length<8)
                    {
                        return;
                    }
                    else
                    {
                        payload_len = (UInt16)(userToken.tempBuffer[2] << 8 | userToken.tempBuffer[3]);
                        if (userToken.tempBuffer.Length<(8+ payload_len))
                        {
                            return;
                        }
                        else
                        {
                            int qunum = 8 + payload_len;
                            int lxnum = userToken.tempBuffer.Length - qunum;
                            r = new byte[qunum];
                            Array.Copy(userToken.tempBuffer, 0, r, 0, qunum);
                            byte[] t = new byte[lxnum];
                            Array.Copy(userToken.tempBuffer, qunum, t, 0, lxnum);
                            userToken.tempBuffer = t;
                        }
                    }
                }
                else if (payload_len == 127)
                {
                    if (userToken.tempBuffer.Length < 14)
                    {
                        return;
                    }
                    else
                    {
                        byte[] uInt64Bytes = new byte[8];
                        for (int i = 0; i < 8; i++)
                        {
                            uInt64Bytes[i] = userToken.tempBuffer[9 - i];
                        }
                        UInt64 len = BitConverter.ToUInt64(uInt64Bytes, 0);
                        payload_len = (int)len;
                        if (userToken.tempBuffer.Length < (14 + payload_len))
                        {
                            return;
                        }
                        else
                        {
                            int qunum = 14 + payload_len;
                            int lxnum = userToken.tempBuffer.Length - qunum;
                            r = new byte[qunum];
                            Array.Copy(userToken.tempBuffer, 0, r, 0, qunum);
                            byte[] t = new byte[lxnum];
                            Array.Copy(userToken.tempBuffer, qunum, t, 0, lxnum);
                            userToken.tempBuffer = t;
                        }
                    }
                }
                else
                {
                    if (userToken.tempBuffer.Length < 6)
                    {
                        return;
                    }
                    else
                    {
                        int qunum = 6 + payload_len;
                        int lxnum = userToken.tempBuffer.Length - qunum;
                        r = new byte[qunum];
                        Array.Copy(userToken.tempBuffer, 0, r, 0, qunum);
                        byte[] t = new byte[lxnum];
                        Array.Copy(userToken.tempBuffer, qunum, t, 0, lxnum);
                        userToken.tempBuffer = t;
                    }
                }
            }
            int opcode = r[0] & 0x0F;
            if (opcode==0x8)
            {
                //e.UserToken = null;
                server.CloseClient(e);
                return;
            }

            var segList = userToken.segList;
            //string mgs = string.Empty;
            byte[] all = null;
            if ((r[0] & 0x80) == 0)
            {
                segList.Add(new ArraySegment<byte>(r, 0, r.Length));
            }
            else
            {
                if (segList.Count == 0)
                {
                    all = WebSocketSeverHelper.DecodeClientData(r, r.Length);
                }
                else
                {
                    for (int i = 0; i < segList.Count; i++)
                    {
                        if (all == null)
                        {
                            all = WebSocketSeverHelper.DecodeClientByteData(segList[i].Array, segList[i].Count);
                        }
                        else
                        {
                            var pit = WebSocketSeverHelper.DecodeClientByteData(segList[i].Array, segList[i].Count);
                            all = all.Concat(pit).ToArray();
                        }
                    }
                    //mgs = Encoding.UTF8.GetString(all);
                    segList.Clear();
                }
                //Console.WriteLine("ReceiveProtocol : " + mgs);
                WsDataOn(all, e);
            }
        }

        public abstract void WsDataOn(byte[] data, SocketAsyncEventArgs e);
        public abstract void WsConnected(SocketAsyncEventArgs e);
        public abstract void WsDisConnected(SocketAsyncEventArgs e);
        public void WsSend(string msg, SocketAsyncEventArgs e) 
        {
            byte[] a = WebSocketSeverHelper.EncodeServerData(msg);
            server.SendMessage(e, a);
        }
        public void WsClientClose(SocketAsyncEventArgs e) 
        {
            server.CloseClient(e);
        }
    }
}
